package br.org.bertol.mestrado.engine.search;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;

import br.org.bertol.mestrado.Verify;
import br.org.bertol.mestrado.engine.Objective;
import br.org.bertol.mestrado.engine.optimisation.hillclimbing.Path;
import br.org.bertol.mestrado.engine.optimisation.moo.ParetoDominace;

public class Greedy {
    private Verify                            verify;

    private ArrayList<Path>                   paths;

    private final boolean                     bestFirst;

    private final EnumMap<Objective, Integer> emptySet;

    private final ParetoDominace              paretoDominace;

    public Greedy(final boolean bestFirst) {
        paths = new ArrayList<Path>();

        this.bestFirst = bestFirst;

        emptySet = new EnumMap<Objective, Integer>(Objective.class);

        for (Objective objective : Objective.values())
            emptySet.put(objective, 0);

        paretoDominace = new ParetoDominace();
    }

    /**
     * Realiza uma busca gulosa para uma matriz de entrada.
     * @param classes
     *            - Classes de entrada para a busca
     * @return Lista contendo solu��es para as classes de entrada
     * @throws CloneNotSupportedException
     */
    @SuppressWarnings("unchecked")
    public List<Path> greedySearch(final List<Integer> classes)
            throws CloneNotSupportedException {
        if (paths.size() == 0) {
            for (int c : classes) {
                final Path caminho = new Path();

                // verifica precedencia para a primeira
                // classe do caminho
                caminho.addClass(c);

                verify.verifyStrongConstraint(caminho, false);

                // descarta os caminhos inv�lidos
                if (caminho.isValidPath())
                    paths.add(search(caminho, classes));
            }
        }

        return (List<Path>) paths.clone();
    }

    private Path search(final Path caminho, final List<Integer> classes)
            throws CloneNotSupportedException {
        final List<Path> caminhos = new ArrayList<Path>();

        for (int c : classes) {
            final Path candidato = (Path) caminho.clone();

            if (candidato.addClass(c)) {
                verify.verifyStrongConstraint(candidato, false);

                // descarta os caminhos inv�lidos
                if (candidato.isValidPath()) {
                    // zera peso parcial
                    candidato.setHeight(emptySet.clone());
                    // verifica pesos para ocorrencia
                    for (Objective objective : verify.getObjectives())
                        verify
                                .verifyWeekConstraint(candidato, false,
                                                      objective);
                    caminhos.add(candidato);
                }
            }
        }

        // verifica qual dos itens tem o menor peso
        Path choosenOne = new Path();

        for (Path postulante : caminhos) {
            if (choosenOne.isValidPath() && bestFirst)
                break;

            // verifica se � melhor
            if (paretoDominace.compare(postulante.getHeight(), choosenOne
                    .getHeight()) == -1)
                choosenOne = (Path) postulante.clone();
        }

        // soma peso do caminho at� com o do candidato
        if (caminho.getRoute().size() > 1)
            choosenOne.sumHeigtht(caminho.getHeight());

        // crit�rio de parada. se percorreu todos as inst�ncias,
        // para tudo e volta solu��o de menor peso
        if (choosenOne.getRoute().size() == classes.size())
            return choosenOne;

        // caso contr�rio, continua
        return search(choosenOne, classes);
    }

    /**
     * @param verify
     *            the verify to set
     */
    public void setVerify(Verify verify) {
        this.verify = verify;
    }
}
